home *** CD-ROM | disk | FTP | other *** search
-
- /* Max holes per side of the peg board. */
- #define HOLES 13
-
- void GetPerimeter( Point thePegs[], short numPegs,
- Point outerPegs[], short sideLast[] );
- void GetEdgePegs( Point outerPegs[], short test, short last,
- Point edgePegs[], short *numEdgePegs );
- void CheckEdgePegs( Point edgePegs[], short *numEdgePegs,
- Point newPeg, short first );
- void IntegrateArea( Point edgePegs[], short numEdgePegs,
- Fixed *area );
-
- /*******************************************************/
-
- /* BandedPegs takes an array of points representing
- * pegs on a pegboard and returns an array of points
- * representing the pegs that would be touched by a
- * rubber band surrounding as many pegs as possible.
- * It also returns the area thus surrounded. */
-
- void BandedPegs( short numPegs, Point thePegs[],
- short *numEdgePegs, Point edgePegs[], Fixed *area )
- {
- Point outerPegs[4*(HOLES-1)+1];
- short sideLast[4], first, last, i;
-
- if( numPegs > 3 ) {
-
- GetPerimeter( thePegs, numPegs, outerPegs, sideLast );
-
- /* Initialize some variables and march around
- * the sides of the board. */
- *numEdgePegs = first = i = 0;
- do {
- /* If there's at least one new peg along the
- * column tops (bottoms), see which ones contact
- * the rubber band. */
- last = sideLast[i++];
- if( first < last ) {
- GetEdgePegs( outerPegs, first, last, edgePegs,
- numEdgePegs );
- first = last;
- }
- /* Count all pegs from the last (first) column
- * as edge pegs. */
- last = sideLast[i++];
- while( first < last )
- edgePegs[(*numEdgePegs)++] = outerPegs[first++];
- } while( i < 4 ); /* Repeat for four sides. */
- }
- else {
- /* With 3 or fewer pegs, all will touch the rubber band. */
- *numEdgePegs = numPegs;
- for( i = 0; i < numPegs; i++ ) edgePegs[i] = thePegs[i];
- if( numPegs < 3 ) {
- /* With less than 3 pegs, area must be 0. */
- *area = 0;
- return;
- }
- }
-
- IntegrateArea( edgePegs, *numEdgePegs, area );
-
- /* If there are more than 3 pegs, and they are all
- * in a straight line (indicated by a zero area),
- * the above algorithm will have counted the interior
- * points twice. The following will remove the
- * redundant set of interior points. Note that
- * it's also okay for 3 pegs, but no fewer. */
- if( *area == 0 )
- *numEdgePegs = (*numEdgePegs + 3)/2;
- }
-
- /*******************************************************/
-
- /* This function finds the pegs which roughly
- * define the four sides of the rubber band. */
-
- void GetPerimeter( Point thePegs[], short numPegs,
- Point outerPegs[], short sideLast[] )
- {
- short colmin[HOLES], colmax[HOLES],
- rowmin[HOLES], rowmax[HOLES],
- col, row, col1, col2, n;
-
- for( n = 0; n < HOLES; n++ ) {
- colmin[n] = rowmin[n] = HOLES;
- colmax[n] = rowmax[n] = -1;
- }
- /* Check each peg to see if it sets a new extreme
- * in any row or column. */
- for( n = 0; n < numPegs; n++ ) {
- row = thePegs[n].v;
- col = thePegs[n].h;
- if( col < colmin[row] ) colmin[row] = col;
- if( col > colmax[row] ) colmax[row] = col;
- if( row < rowmin[col] ) rowmin[col] = row;
- if( row > rowmax[col] ) rowmax[col] = row;
- }
- /* Collect the pegs at the tops of each column. */
- n = -1;
- for( col = 0; col < HOLES; col++ ) {
- if( (row = rowmin[col]) < HOLES ) {
- outerPegs[++n].v = row;
- outerPegs[n].h = col;
- }
- }
- sideLast[0] = n;
- col1 = outerPegs[0].h;
- col2 = outerPegs[n].h;
- /* Collect all but the top peg of the last column,
- * from top to bottom. */
- for( row = rowmin[col2] + 1; row <= rowmax[col2]; row++ ) {
- if( colmax[row] == col2 ) {
- outerPegs[++n].v = row;
- outerPegs[n].h = col2;
- }
- }
- sideLast[1] = n;
- /* From last to first, collect the pegs at the
- * bottoms of all but the last column. */
- for( col = col2 - 1; col >= col1; col-- ) {
- if( (row = rowmax[col]) >= 0 ) {
- outerPegs[++n].v = row;
- outerPegs[n].h = col;
- }
- }
- sideLast[2] = n;
- /* Collect all but the bottom peg of the first column,
- * from bottom to top. */
- for( row = rowmax[col1] - 1; row >= rowmin[col1]; row-- ) {
- if( colmin[row] == col1 ) {
- outerPegs[++n].v = row;
- outerPegs[n].h = col1;
- }
- }
- sideLast[3] = n;
- }
-
- /*******************************************************/
-
- /* This function finds the pegs which would push
- * a rubber band to the left of a line between a
- * given starting point and a given ending point.
- * It counts the starting point (but not the
- * ending point) as such a peg. */
-
- void GetEdgePegs( Point outerPegs[], short test, short last,
- Point edgePegs[], short *numEdgePegs )
- {
- Point testPeg, backPeg, nextPeg;
- short convex, first;
-
- first = *numEdgePegs;
-
- backPeg = edgePegs[(*numEdgePegs)++] = outerPegs[test];
- nextPeg = outerPegs[last];
- /* Loop through the array of outerPegs from the
- * one after the starting point to the one just
- * before the ending point. */
- while( ++test < last ) {
- testPeg = outerPegs[test];
- /* See if the path connecting backPeg, testPeg,
- * and nextPeg is convex, straight, or concave. */
- if( (convex = (nextPeg.v-backPeg.v)*(testPeg.h-backPeg.h)
- -(testPeg.v-backPeg.v)*(nextPeg.h-backPeg.h)) >= 0 ) {
- /* If convex or straight, count the test
- * peg as an edge peg. */
- edgePegs[(*numEdgePegs)++] = backPeg = testPeg;
- /* If convex, the rubber band's path will change,
- * so we need to check previous edge pegs to see
- * if they are still edge pegs. */
- if( convex > 0 )
- CheckEdgePegs( edgePegs, numEdgePegs, testPeg, first );
- }
- }
- }
-
- /*******************************************************/
-
- /* If a peg just added to the list of edge pegs
- * has extended the rubber band, this routine will
- * search backward through the list, throwing out pegs
- * that are no longer contacted, until it finds one
- * that still is. */
-
- void CheckEdgePegs( Point edgePegs[], short *numEdgePegs,
- Point newPeg, short first )
- {
- Point testPeg, backPeg;
- short test;
-
- test = *numEdgePegs - 1;
- /* Loop backward through the list of edge pegs,
- * starting with the one before that just added,
- * stopping before the first that can't be removed. */
- while( --test > first ) {
- testPeg = edgePegs[test];
- backPeg = edgePegs[test-1];
- /* If the path between newPeg, testPeg,
- * and backPeg is concave, remove the peg. */
- if( (newPeg.v-backPeg.v)*(testPeg.h-backPeg.h)
- -(testPeg.v-backPeg.v)*(newPeg.h-backPeg.h) < 0 )
- edgePegs[test] = edgePegs[--(*numEdgePegs)];
- else
- return;
- }
- }
-
- /*******************************************************/
-
- /* This function integrates the area enclosed
- * by a rubber band. */
-
- void IntegrateArea( Point edgePegs[],
- short numEdgePegs, Fixed *area )
- {
- Point thePeg, lastPeg;
- long integral = 0;
- short i;
- /* Starting and ending with the last peg,
- * integrate double the area under the closed path. */
- lastPeg = edgePegs[numEdgePegs-1];
- for( i = 0; i < numEdgePegs; i++ ) {
- thePeg = edgePegs[i];
- integral += (thePeg.h + lastPeg.h)*(thePeg.v - lastPeg.v);
- lastPeg = thePeg;
- }
- /* Correct a negative integral if the path was
- * counterclockwise. */
- if( integral < 0 ) integral = -integral;
- /* By shifting, simultaneously halve the integral
- * and convert it to a fixed. */
- *area = (Fixed)( integral << 15 );
- }
-